package com.sneaker.shower.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sneaker.shower.common.BizException;
import com.sneaker.shower.entity.domain.order.PayOrderDO;
import com.sneaker.shower.entity.domain.user.UserLevelDO;
import com.sneaker.shower.entity.dto.WxPayParamDTO;
import com.sneaker.shower.entity.enums.UserTypeEnum;
import com.sneaker.shower.mapper.UserLevelMapper;
import com.sneaker.shower.service.UserLevelService;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.List;

@Service
public class UserLevelServiceImpl extends ServiceImpl<UserLevelMapper, UserLevelDO> implements UserLevelService {

    private UserLevelDO getHighestLevel(Long userGuid) {
        List<UserLevelDO> list = this.list(new LambdaQueryWrapper<UserLevelDO>()
                .eq(UserLevelDO::getUserGuid, userGuid)
                .gt(UserLevelDO::getLevelAmount, BigDecimal.ZERO)
                .orderByDesc(UserLevelDO::getLevelCode));
        return CollectionUtils.isEmpty(list) ?
                UserLevelDO.builder()
                        .levelAmount(BigDecimal.ZERO)
                        .levelCode(UserTypeEnum.NORMAL.getCode())
                        .levelDiscount(UserTypeEnum.NORMAL.getDiscount())
                        .build()
                : list.get(0);
    }

    @Override
    public Pair<UserLevelDO, BigDecimal> updateUserLevel(PayOrderDO payOrderDO, WxPayParamDTO wxPayParamDTO) {
        Long userGuid = payOrderDO.getUserGuid();
        LambdaQueryWrapper<UserLevelDO> wrapper = new LambdaQueryWrapper<UserLevelDO>()
                .eq(UserLevelDO::getUserGuid, payOrderDO.getUserGuid());
        UserLevelDO one;
        if (payOrderDO.getType() == 1) {
            int code = Integer.parseInt(payOrderDO.getAttach());
            wrapper.eq(UserLevelDO::getLevelCode, code);
            one = this.getOne(wrapper);
            if (one == null) {
                one = new UserLevelDO();
                one.setGuid(IdWorker.getId());
                one.setUserGuid(userGuid);
                one.setLevelCode(code);
                one.setLevelDiscount(UserTypeEnum.getByCode(code).getDiscount());
                one.setVersion(0);
            } else {
                one.setVersion(wxPayParamDTO.getUserLevelVersion());
            }
            BigDecimal levelAmount = one.getLevelAmount() == null ? BigDecimal.ZERO : one.getLevelAmount();
            one.setLevelAmount(levelAmount.add(payOrderDO.getTotalFee()));
        } else {
            wrapper.eq(UserLevelDO::getGuid, Long.parseLong(wxPayParamDTO.getUserLevelGuid()));
            one = this.getOne(wrapper);
            if (one == null) {
                log.error("交易失败，用户信息已发生变化");
                throw new BizException("用户账户已发生变化");
            }
            if (one.getLevelAmount() == null || one.getLevelAmount().compareTo(BigDecimal.ZERO) <= 0) {
                throw new BizException("支付失败,会员卡余额不足");
            }
            BigDecimal amount = one.getLevelAmount().subtract(payOrderDO.getTotalFee().subtract(payOrderDO.getActuallyPayFee()));
            if (amount.compareTo(BigDecimal.ZERO) < 0) {
                throw new BizException("支付失败，会员卡余额不足");
            }
            one.setLevelAmount(amount);
        }
        if (!saveOrUpdate(one)) {
            log.error("交易失败，用户信息已发生变化");
            throw new BizException("用户账户已发生变化");
        }
        UserLevelDO highestLevel = this.getHighestLevel(userGuid);
        return getUserLevelBigDecimalPair(userGuid, highestLevel);
    }

    @Override
    public Pair<UserLevelDO, BigDecimal> updateUserPayLevel(UserLevelDO userLevelDO, Long userGuid) {
        if (!this.updateById(userLevelDO)) {
            throw new BizException("会员余额已发生变化");
        }
        UserLevelDO highestLevel = getHighestLevel(userGuid);
        return getUserLevelBigDecimalPair(userGuid, highestLevel);
    }

    @Override
    public Pair<UserLevelDO, BigDecimal> getUserLevelBigDecimalPair(Long userGuid, UserLevelDO highestLevel) {
        BigDecimal totalAmount = this.list(new LambdaQueryWrapper<UserLevelDO>()
                .eq(UserLevelDO::getUserGuid, userGuid)).stream().map(UserLevelDO::getLevelAmount)
                .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
        return Pair.of(highestLevel, totalAmount);
    }
}
